package database.datastructure;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import database.DataFile;
import database.DataManager;
import database.Index;
import database.pojo.ConditionObj;

public class ThresholdImpl {

	Map<String, Map<Integer,Integer>> sortedMap = new HashMap<String, Map<Integer,Integer>>();
	Map<String, List<Integer>> condListMap = new HashMap<String,List<Integer>>();
	TreeMap<Integer, Integer> resultMap = new TreeMap<Integer, Integer>(); // Contains <RecordId, Value>	
	private ArrayList<Integer> topkRecordIds = null;
	private int topElementsCounter = 0;
	private int topk = 0;
	int curThreshold = 0;
	private ArrayList<String> attributes = null;
	private Map<String, Integer> conditionMap = new HashMap<String, Integer>();
	private TreeMap<Integer, Integer> topkRecordIdMap = null; // Stores Final top K list <RecordId, RecExpressionResult>
	private boolean isDescending = false;
	private String tableName;
	TreeMap<Integer, Integer> unOrderedMap = new TreeMap<Integer, Integer>();
	private boolean isWhere = false;
	
	public ThresholdImpl(ConditionObj condObj) {
		
		Map<Integer, Map<String, String>> records = condObj.getRecords();
		String expression = condObj.getExpression();
		int topk = condObj.getTopkValue();
		this.tableName = condObj.getTableName();
		
		ExpressionEval exprEval = new ExpressionEval();
		/*Map<String, Integer> */conditionMap = exprEval.evaluateExpression(expression);
		ArrayList<String> attributes = exprEval.getConditionAttr();			
		
		if (condObj.isDescending())
			isDescending = true;
		else
			isDescending = false;
		this.attributes = attributes;
		this.topk = topk;
		this.isWhere = condObj.isWhere();
		
		Iterator<String> attributesIter = attributes.iterator();
		try {
			if (isWhere) {
				while (attributesIter.hasNext()) {
					String curAttr = (String)attributesIter.next();
					BPlusTree bTree = new BPlusTree();
					for (int recordId: records.keySet()) {
						Map<String, String> record = records.get(recordId);
						bTree.insert(record.get(curAttr), recordId);
					}
					if (isDescending) {
						bTree.generateDecSortedList();
					} else {
						bTree.generateIncSortedList();
					}
					
					sortedMap.put(curAttr, bTree.getSortedMap());
					condListMap.put(curAttr, bTree.getSortedValues());
				}
			} else {
				DataManager dataMan = new DataManager();
				DataFile dataFile = dataMan.getFile(tableName);
				Map<String, String> columnIndexMap = dataFile.columnIndexMap;
				while (attributesIter.hasNext()) { 
					String curAttr = (String)attributesIter.next();
					if (columnIndexMap.containsKey(curAttr)) {
						try {
							String usrIdxName = columnIndexMap.get(curAttr);
							//System.out.println("DataFile Name : "+dataFile.fileName);
							Index idx = dataFile.restoreIndex(usrIdxName);
							//System.out.println("User Index Name : "+usrIdxName);
							BPlusTree bTree = idx.getbTree();
							
							if (isDescending) {
								bTree.generateDecSortedList();
							} else {
								bTree.generateIncSortedList();
							}
							
							sortedMap.put(curAttr, bTree.getSortedMap());
							condListMap.put(curAttr, bTree.getSortedValues());
						} catch(Exception e) {
							//e.printStackTrace();
						}
						
					} else {
						BPlusTree bTree = new BPlusTree();
						for (int recordId: records.keySet()) {
							Map<String, String> record = records.get(recordId);
							bTree.insert(record.get(curAttr), recordId);
						}
						if (isDescending) {
							bTree.generateDecSortedList();
						} else {
							bTree.generateIncSortedList();
						}
						
						sortedMap.put(curAttr, bTree.getSortedMap());
						condListMap.put(curAttr, bTree.getSortedValues());
					}
				}
			}
			//If user requested all the records
			if (topk > records.size()) {
				
			} else {
				int recordCurPos = 0;
				ThresholdAlgorithm(recordCurPos++);
				while (!(isThresHoldMet())) {
					ThresholdAlgorithm(recordCurPos++);
				}
			}
			
		} catch (Exception e) {
			//e.printStackTrace();
		}
	}
	
	public void ThresholdAlgorithm(int curPos) {
		// TODO: temp code [sravan]
		try {
			curThreshold = 0;
			for (int i = 0; i < attributes.size(); i++) {
				List<Integer> recordIdList = condListMap.get(attributes.get(i));
				int curRecId = recordIdList.get(curPos);
				Map<Integer, Integer> valueMap = sortedMap.get(attributes.get(i));
				int arg1 =  Integer.parseInt(valueMap.get(curRecId)+"");
				int arg2 = (conditionMap.get(attributes.get(i))).intValue();
				curThreshold = curThreshold + (arg1 * arg2);
			}
			
			for (int i = 0; i < attributes.size(); i++) {
				String curAttr = attributes.get(i);
				List<Integer> recordIds= condListMap.get(curAttr);
				int curRecord = recordIds.get(curPos);
				Map<Integer, Integer> curMap = sortedMap.get(curAttr);
				int arg1 = Integer.parseInt((curMap.get(curRecord))+"");
				int arg2 = Integer.parseInt(conditionMap.get(curAttr)+"");
				int curValue = arg1 * arg2;

				ArrayList<String> secAttributes = new ArrayList<String>(attributes);
				secAttributes.remove(curAttr);
				for (int j = 0; j < secAttributes.size(); j++) {
					String remCurAttr = secAttributes.get(j);
					Map<Integer, Integer> remMap = sortedMap.get(remCurAttr);
					int arg11 = 0;
					if (remMap.get(curRecord) != null) 
						arg11 = Integer.parseInt((remMap.get(curRecord))+"");
					int arg12 = Integer.parseInt(conditionMap.get(remCurAttr) + "");
					curValue = curValue + arg11 * arg12;
				}
				resultMap.put(curRecord, curValue);
			}
		} catch(Exception e) {
			//e.printStackTrace();
		}
	}

	public boolean isThresHoldMet() {
		//System.out.println("isThresHoldMet : curThreshold="+curThreshold + ", resultMap.size()="+resultMap.size());
		topElementsCounter = 0;
		topkRecordIds = new ArrayList<Integer>();
		
		if (isDescending) {
			DecreasingOrderer decOrdr = new DecreasingOrderer(unOrderedMap);
			topkRecordIdMap = new TreeMap(decOrdr);
		}
		else {
			IncreasingOrderer incOrdr = new IncreasingOrderer(unOrderedMap);
			topkRecordIdMap = new TreeMap(incOrdr);
		}
		
		for (int k: resultMap.keySet()) {
			int v = resultMap.get(k);
			//System.out.println("<"+k+", "+v+"> extracted from resultMap with size"+resultMap.size());
			if (isDescending) {
				if (v >= curThreshold) {
					topElementsCounter++;
					topkRecordIds.add(k);
					//System.out.println("<"+k+", "+v+"> added to unOrderedMap");
					unOrderedMap.put(k, v);
				} else {
					continue;
				}
			} else {
				if (v <= curThreshold) {
					topElementsCounter++;
					topkRecordIds.add(k);
					//System.out.println("<"+k+", "+v+"> added to unOrderedMap");
					unOrderedMap.put(k, v);
				} else {
					continue;
				}
			}
			/*if (v >= curThreshold) {
				topElementsCounter++;
				topkRecordIds.add(k);
				//System.out.println("<"+k+", "+v+"> added to unOrderedMap");
				unOrderedMap.put(k, v);
			} else {
				continue;
			}*/
		}
		if (unOrderedMap.size() >= topk) {
			//System.out.println("Inside ifff");
			int counter = 0;
			if (isDescending) {
				int startpos = (unOrderedMap.size()-topk);
				for (Entry<Integer, Integer> e : unOrderedMap.entrySet()) {
					if (counter >= startpos) {
						//System.out.println("<"+e.getKey()+", "+e.getValue()+">");
						topkRecordIdMap.put(e.getKey(), e.getValue());
						counter++;
					} else {
						counter++;
						continue;
					}
				}
			} else {
				for (Entry<Integer, Integer> e : unOrderedMap.entrySet()) {
					if (counter == topk) {
						break;
					} else {
						topkRecordIdMap.put(e.getKey(), e.getValue());
						counter++;
					}
				}
			}
			//topkRecordIdMap.putAll(unOrderedMap);
			return true;
		}
			
		return false;
	}

	public ArrayList<Integer> getTopkRecordIds() {
		return topkRecordIds;
	}

	public Map<Integer, Integer> getTopkRecordIdMap() {
		return topkRecordIdMap;
	}
	
	/*public class MaxPriorityQueueInteger<K,V extends Comparable<V>> implements Comparator<KeyValuePair<K, V>>{

		@Override
		public int compare(
				KeyValuePair<K, V> o1,
				KeyValuePair<K, V> o2) {
			
			return o2.value - o1.value;
		}
		
	}
	
	private class KeyValuePair<K,V>{
		public K key;
		public V value;
		
		public KeyValuePair(K key, V value){
			this.key = key;
			this.value = value;
		}
	}*/
	
	class IncreasingOrderer implements Comparator {
		
		Map incOrdrMap;
		IncreasingOrderer(Map tmpMap) {
			incOrdrMap = tmpMap;
		}
		
		@Override
		public int compare(Object arg0, Object arg1) {
			int returnValue = 0;
			try {
				if ((Integer)incOrdrMap.get(arg0) >= (Integer)incOrdrMap.get(arg1))
					returnValue = 1;
				/*else if ((Integer)incOrdrMap.get(arg0) == (Integer)incOrdrMap.get(arg1))
					returnValue = 0;*/
				else 
					returnValue = -1;
			} catch(Exception e) {
				//e.printStackTrace();
			}
			return returnValue;
		}
		
	}
	
	class DecreasingOrderer implements Comparator {
		
		Map decOrdrMap;
		DecreasingOrderer(Map tmpMap) {
			decOrdrMap = tmpMap;
		}
		
		@Override
		public int compare(Object arg0, Object arg1) {
			int returnValue = 0;
			try {
				//System.out.println("Values compared: "+(Integer)decOrdrMap.get(arg0)+" : "+(Integer)decOrdrMap.get(arg1));
				if ((Integer)decOrdrMap.get(arg0) <= (Integer)decOrdrMap.get(arg1))
					returnValue = 1;
				/*else if ((Integer)decOrdrMap.get(arg0) == (Integer)decOrdrMap.get(arg1))
					returnValue = 0;*/
				else 
					returnValue = -1;
			} catch(Exception e) {
				//e.printStackTrace();
			}
			return returnValue;
		}
		
	}
	
	public void returnAllRecords() {
		
	}
}
